package echo;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @date： 2025/4/1 16:47
 * @description： TODO
 * @modifiedBy：
 * @version: 1.0
 */
public class NioEchoServer {

    public static void main(String[] args) {
        try {
            // 1. 创建Selector(多路复用)，用于监听所有Channel的事件
            Selector selector = Selector.open();
            // 2. 创建ServerSocketChannel并配置为非阻塞模式
            ServerSocketChannel serverChannel = ServerSocketChannel.open();
            serverChannel.socket().bind(new InetSocketAddress(8080)); // 绑定监听8080端口
            serverChannel.configureBlocking(false); // 设置为非阻塞模式
            // 3. 将ServerSocketChannel注册到Selector注册到Selector，关注OP_ACCEPT事件(新连接)
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("Server started on port 8080");
            // 4. 事件循环：持续处理I/O事件
            while (true) {
                // 4.1 阻塞直到有事件就绪（新连接、读、写）
                selector.select();
                // 4.2 获取所有就绪事件的SelectioniKey集合
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
                while (keyIterator.hasNext()) {
                    SelectionKey key = keyIterator.next();
                    keyIterator.remove(); // 必须手动移除，防止重复的处理
                    try {
                        // 5. 处理新连接事件
                        if (key.isAcceptable()) {
                            handleAccept(key, selector);
                        }
                        // 6. 处理读事件(客户端发送数据)
                        else if (key.isReadable()) {
                            handleRead(key);
                        }
                        // 7. 处理写事件(回显事件)
                        else if (key.isWritable()) {
                            handleWrite(key);
                        }
                    } catch (Exception e) {
                        // 8. 异常处理：关闭发生故障的channel
                        key.channel().close();
                        System.out.println("Connection closed due to error: " + e.getMessage());
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 处理新连接的方法
    private static void handleAccept(SelectionKey key, Selector selector) throws Exception {
        // 5.1 获取触发事件的ServerSocketChannel
        ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
        // 5.2 接受客户端连接
        SocketChannel clientChannel = serverChannel.accept();
        clientChannel.configureBlocking(false); // 设置为非阻塞模式
        // 5.3 注册客户端Channel到Selector，关注OP_READ事件
        clientChannel.register(selector, SelectionKey.OP_READ);
        System.out.println("Accepted connection from: " + clientChannel.getRemoteAddress());
        // 5.4 关闭ServerSocketChannel，不再接受新连接（见服务单个客户端）
        serverChannel.close(); // 关键操作：确保单链接限制
    }

    private static void handleRead(SelectionKey key) throws IOException {
        // 6.1 获取触发事件的ServerSocketChannel
        SocketChannel clientChannel = (SocketChannel) key.channel();
        // 6.2 创建缓冲区读取数据
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int bytesRead = clientChannel.read(buffer);
        // 6.3 处理连接关闭的清空(返回-1)
        if (bytesRead == -1) {
            clientChannel.close();
            System.out.println("client disconnected");
            return;
        }
        // 6.4 切换缓冲区为读模式
        buffer.flip();
        // 6.5 将数据从缓冲区复制到字节数组
        byte[] data = new byte[buffer.remaining()];
        buffer.get(data);
        String message = new String(data);
        System.out.println("Received: " + message);

        // 6.6 修改关注事件为OP_WRITE(准备写回数据)
        key.interestOps(SelectionKey.OP_WRITE);

        // 6.7 将数据附加到SelectionKey,供写操作使用
        key.attach(ByteBuffer.wrap(data));

    }

    private static void handleWrite(SelectionKey key) throws IOException {
        // 7.1 获取客户端SocketChannel
        SocketChannel clientChannel = (SocketChannel) key.channel();

        // 7.2 从附件中获取待写数据
        ByteBuffer buffer = (ByteBuffer) key.attachment();

        if (buffer != null) {
            // 7.3 将数据写回客户端
            clientChannel.write(buffer);

            // 7.4 检查是否还有剩余数据未写入
            if (!buffer.hasRemaining()) {
                // 7.4.1 数据全部写入后,重新关注OP_READ事件
                key.interestOps(SelectionKey.OP_READ);
                key.attach(null); // 清理附件
            }
        }


    }


}